/* do not edit automatically generated by mc from nameKey.  */
/* nameKey.mod provides a dynamic binary tree name to key.

Copyright (C) 2015-2023 Free Software Foundation, Inc.
Contributed by Gaius Mulley <gaius@glam.ac.uk>.

This file is part of GNU Modula-2.

GNU Modula-2 is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.

GNU Modula-2 is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.

You should have received a copy of the GNU General Public License
along with GNU Modula-2; see the file COPYING3.  If not see
<http://www.gnu.org/licenses/>.  */

#include "config.h"
#include "system.h"
#include <stdbool.h>
#   if !defined (PROC_D)
#      define PROC_D
       typedef void (*PROC_t) (void);
       typedef struct { PROC_t proc; } PROC;
#   endif

#   if !defined (TRUE)
#      define TRUE (1==1)
#   endif

#   if !defined (FALSE)
#      define FALSE (1==0)
#   endif

#   include "GStorage.h"
#   include "Gmcrts.h"
#if defined(__cplusplus)
#   undef NULL
#   define NULL 0
#endif
#define _nameKey_H
#define _nameKey_C

#   include "GSYSTEM.h"
#   include "GStorage.h"
#   include "GIndexing.h"
#   include "GStrIO.h"
#   include "GStdIO.h"
#   include "GNumberIO.h"
#   include "GStrLib.h"
#   include "Glibc.h"
#   include "GASCII.h"
#   include "GM2RTS.h"

#   define nameKey_NulName 0
typedef unsigned int nameKey_Name;

typedef struct nameKey__T1_r nameKey__T1;

typedef char *nameKey_ptrToChar;

typedef nameKey__T1 *nameKey_nameNode;

typedef enum {nameKey_less, nameKey_equal, nameKey_greater} nameKey_comparison;

struct nameKey__T1_r {
                       nameKey_ptrToChar data;
                       nameKey_Name key;
                       nameKey_nameNode left;
                       nameKey_nameNode right;
                     };

static nameKey_nameNode binaryTree;
static Indexing_Index keyIndex;
static unsigned int lastIndice;

/*
   makeKey - returns the Key of the symbol, a. If a is not in the
             name table then it is added, otherwise the Key of a is returned
             directly. Note that the name table has no scope - it merely
             presents a more convienient way of expressing strings. By a Key.
*/

extern "C" nameKey_Name nameKey_makeKey (const char *a_, unsigned int _a_high);

/*
   makekey - returns the Key of the symbol, a. If a is not in the
             name table then it is added, otherwise the Key of a is returned
             directly. Note that the name table has no scope - it merely
             presents a more convienient way of expressing strings. By a Key.
             These keys last for the duration of compilation.
*/

extern "C" nameKey_Name nameKey_makekey (void * a);

/*
   getKey - returns the name, a, of the key, Key.
*/

extern "C" void nameKey_getKey (nameKey_Name key, char *a, unsigned int _a_high);

/*
   lengthKey - returns the StrLen of Key.
*/

extern "C" unsigned int nameKey_lengthKey (nameKey_Name key);

/*
   isKey - returns TRUE if string, a, is currently a key.
           We dont use the Compare function, we inline it and avoid
           converting, a, into a String, for speed.
*/

extern "C" bool nameKey_isKey (const char *a_, unsigned int _a_high);

/*
   keyToCharStar - returns the C char * string equivalent for, key.
*/

extern "C" void nameKey_writeKey (nameKey_Name key);

/*
   isSameExcludingCase - returns TRUE if key1 and key2 are
                         the same. It is case insensitive.
                         This function deliberately inlines CAP for speed.
*/

extern "C" bool nameKey_isSameExcludingCase (nameKey_Name key1, nameKey_Name key2);

/*
   keyToCharStar - returns the C char * string equivalent for, key.
*/

extern "C" void * nameKey_keyToCharStar (nameKey_Name key);

/*
   doMakeKey - finds the name, n, in the tree or else create a name.
               If a name is found then the string, n, is deallocated.
*/

static nameKey_Name doMakeKey (nameKey_ptrToChar n, unsigned int higha);

/*
   compare - return the result of Names[i] with Names[j]
*/

static nameKey_comparison compare (nameKey_ptrToChar pi, nameKey_Name j);

/*
   findNodeAndParentInTree - search BinaryTree for a name.
                             If this name is found in the BinaryTree then
                             child is set to this name and father is set to the node above.
                             A comparison is returned to assist adding entries into this tree.
*/

static nameKey_comparison findNodeAndParentInTree (nameKey_ptrToChar n, nameKey_nameNode *child, nameKey_nameNode *father);


/*
   doMakeKey - finds the name, n, in the tree or else create a name.
               If a name is found then the string, n, is deallocated.
*/

static nameKey_Name doMakeKey (nameKey_ptrToChar n, unsigned int higha)
{
  nameKey_comparison result;
  nameKey_nameNode father;
  nameKey_nameNode child;
  nameKey_Name k;

  result = findNodeAndParentInTree (n, &child, &father);
  if (child == NULL)
    {
      if (result == nameKey_less)
        {
          Storage_ALLOCATE ((void **) &child, sizeof (nameKey__T1));
          father->left = child;
        }
      else if (result == nameKey_greater)
        {
          /* avoid dangling else.  */
          Storage_ALLOCATE ((void **) &child, sizeof (nameKey__T1));
          father->right = child;
        }
      child->right = NULL;
      child->left = NULL;
      lastIndice += 1;
      child->key = lastIndice;
      child->data = n;
      Indexing_PutIndice (keyIndex, child->key, reinterpret_cast<void *> (n));
      k = lastIndice;
    }
  else
    {
      Storage_DEALLOCATE (reinterpret_cast<void **> (&n), higha+1);
      k = child->key;
    }
  return k;
  /* static analysis guarentees a RETURN statement will be used before here.  */
  __builtin_unreachable ();
}


/*
   compare - return the result of Names[i] with Names[j]
*/

static nameKey_comparison compare (nameKey_ptrToChar pi, nameKey_Name j)
{
  nameKey_ptrToChar pj;
  char c1;
  char c2;

  pj = static_cast<nameKey_ptrToChar> (nameKey_keyToCharStar (j));
  c1 = (*pi);
  c2 = (*pj);
  while ((c1 != ASCII_nul) || (c2 != ASCII_nul))
    {
      if (c1 < c2)
        {
          return nameKey_less;
        }
      else if (c1 > c2)
        {
          /* avoid dangling else.  */
          return nameKey_greater;
        }
      else
        {
          /* avoid dangling else.  */
          pi += 1;
          pj += 1;
          c1 = (*pi);
          c2 = (*pj);
        }
    }
  return nameKey_equal;
  /* static analysis guarentees a RETURN statement will be used before here.  */
  __builtin_unreachable ();
}


/*
   findNodeAndParentInTree - search BinaryTree for a name.
                             If this name is found in the BinaryTree then
                             child is set to this name and father is set to the node above.
                             A comparison is returned to assist adding entries into this tree.
*/

static nameKey_comparison findNodeAndParentInTree (nameKey_ptrToChar n, nameKey_nameNode *child, nameKey_nameNode *father)
{
  nameKey_comparison result;

  /* firstly set up the initial values of child and father, using sentinal node  */
  (*father) = binaryTree;
  (*child) = binaryTree->left;
  if ((*child) == NULL)
    {
      return nameKey_less;
    }
  else
    {
      do {
        result = compare (n, (*child)->key);
        if (result == nameKey_less)
          {
            (*father) = (*child);
            (*child) = (*child)->left;
          }
        else if (result == nameKey_greater)
          {
            /* avoid dangling else.  */
            (*father) = (*child);
            (*child) = (*child)->right;
          }
      } while (! (((*child) == NULL) || (result == nameKey_equal)));
      return result;
    }
  /* static analysis guarentees a RETURN statement will be used before here.  */
  __builtin_unreachable ();
}


/*
   makeKey - returns the Key of the symbol, a. If a is not in the
             name table then it is added, otherwise the Key of a is returned
             directly. Note that the name table has no scope - it merely
             presents a more convienient way of expressing strings. By a Key.
*/

extern "C" nameKey_Name nameKey_makeKey (const char *a_, unsigned int _a_high)
{
  nameKey_ptrToChar n;
  nameKey_ptrToChar p;
  unsigned int i;
  unsigned int higha;
  char a[_a_high+1];

  /* make a local copy of each unbounded array.  */
  memcpy (a, a_, _a_high+1);

  higha = StrLib_StrLen ((const char *) a, _a_high);
  Storage_ALLOCATE (reinterpret_cast<void **> (&p), higha+1);
  if (p == NULL)
    {
      M2RTS_HALT (-1);  /* out of memory error  */
      __builtin_unreachable ();
    }
  else
    {
      n = p;
      i = 0;
      while (i < higha)
        {
          (*p) = a[i];
          i += 1;
          p += 1;
        }
      (*p) = ASCII_nul;
      return doMakeKey (n, higha);
    }
  ReturnException ("../../gcc-read-write/gcc/m2/mc/nameKey.def", 20, 1);
  __builtin_unreachable ();
}


/*
   makekey - returns the Key of the symbol, a. If a is not in the
             name table then it is added, otherwise the Key of a is returned
             directly. Note that the name table has no scope - it merely
             presents a more convienient way of expressing strings. By a Key.
             These keys last for the duration of compilation.
*/

extern "C" nameKey_Name nameKey_makekey (void * a)
{
  nameKey_ptrToChar n;
  nameKey_ptrToChar p;
  nameKey_ptrToChar pa;
  unsigned int i;
  unsigned int higha;

  if (a == NULL)
    {
      return nameKey_NulName;
    }
  else
    {
      higha = static_cast<unsigned int> (libc_strlen (a));
      Storage_ALLOCATE (reinterpret_cast<void **> (&p), higha+1);
      if (p == NULL)
        {
          M2RTS_HALT (-1);  /* out of memory error  */
          __builtin_unreachable ();
        }
      else
        {
          n = p;
          pa = static_cast<nameKey_ptrToChar> (a);
          i = 0;
          while (i < higha)
            {
              (*p) = (*pa);
              i += 1;
              p += 1;
              pa += 1;
            }
          (*p) = ASCII_nul;
          return doMakeKey (n, higha);
        }
    }
  ReturnException ("../../gcc-read-write/gcc/m2/mc/nameKey.def", 20, 1);
  __builtin_unreachable ();
}


/*
   getKey - returns the name, a, of the key, Key.
*/

extern "C" void nameKey_getKey (nameKey_Name key, char *a, unsigned int _a_high)
{
  nameKey_ptrToChar p;
  unsigned int i;
  unsigned int higha;

  p = static_cast<nameKey_ptrToChar> (nameKey_keyToCharStar (key));
  i = 0;
  higha = _a_high;
  while (((p != NULL) && (i <= higha)) && ((*p) != ASCII_nul))
    {
      a[i] = (*p);
      p += 1;
      i += 1;
    }
  if (i <= higha)
    {
      a[i] = ASCII_nul;
    }
}


/*
   lengthKey - returns the StrLen of Key.
*/

extern "C" unsigned int nameKey_lengthKey (nameKey_Name key)
{
  unsigned int i;
  nameKey_ptrToChar p;

  p = static_cast<nameKey_ptrToChar> (nameKey_keyToCharStar (key));
  i = 0;
  while ((*p) != ASCII_nul)
    {
      i += 1;
      p += 1;
    }
  return i;
  /* static analysis guarentees a RETURN statement will be used before here.  */
  __builtin_unreachable ();
}


/*
   isKey - returns TRUE if string, a, is currently a key.
           We dont use the Compare function, we inline it and avoid
           converting, a, into a String, for speed.
*/

extern "C" bool nameKey_isKey (const char *a_, unsigned int _a_high)
{
  nameKey_nameNode child;
  nameKey_ptrToChar p;
  unsigned int i;
  unsigned int higha;
  char a[_a_high+1];

  /* make a local copy of each unbounded array.  */
  memcpy (a, a_, _a_high+1);

  /* firstly set up the initial values of child, using sentinal node  */
  child = binaryTree->left;
  if (child != NULL)
    {
      do {
        i = 0;
        higha = _a_high;
        p = static_cast<nameKey_ptrToChar> (nameKey_keyToCharStar (child->key));
        while ((i <= higha) && (a[i] != ASCII_nul))
          {
            if (a[i] < (*p))
              {
                child = child->left;
                i = higha;
              }
            else if (a[i] > (*p))
              {
                /* avoid dangling else.  */
                child = child->right;
                i = higha;
              }
            else
              {
                /* avoid dangling else.  */
                if ((a[i] == ASCII_nul) || (i == higha))
                  {
                    /* avoid gcc warning by using compound statement even if not strictly necessary.  */
                    if ((*p) == ASCII_nul)
                      {
                        return true;
                      }
                    else
                      {
                        child = child->left;
                      }
                  }
                p += 1;
              }
            i += 1;
          }
      } while (! (child == NULL));
    }
  return false;
  /* static analysis guarentees a RETURN statement will be used before here.  */
  __builtin_unreachable ();
}


/*
   keyToCharStar - returns the C char * string equivalent for, key.
*/

extern "C" void nameKey_writeKey (nameKey_Name key)
{
  nameKey_ptrToChar s;

  s = static_cast<nameKey_ptrToChar> (nameKey_keyToCharStar (key));
  while ((s != NULL) && ((*s) != ASCII_nul))
    {
      StdIO_Write ((*s));
      s += 1;
    }
}


/*
   isSameExcludingCase - returns TRUE if key1 and key2 are
                         the same. It is case insensitive.
                         This function deliberately inlines CAP for speed.
*/

extern "C" bool nameKey_isSameExcludingCase (nameKey_Name key1, nameKey_Name key2)
{
  nameKey_ptrToChar pi;
  nameKey_ptrToChar pj;
  char c1;
  char c2;

  if (key1 == key2)
    {
      return true;
    }
  else
    {
      pi = static_cast<nameKey_ptrToChar> (nameKey_keyToCharStar (key1));
      pj = static_cast<nameKey_ptrToChar> (nameKey_keyToCharStar (key2));
      c1 = (*pi);
      c2 = (*pj);
      while ((c1 != ASCII_nul) && (c2 != ASCII_nul))
        {
          if (((c1 == c2) || (((c1 >= 'A') && (c1 <= 'Z')) && (c2 == ((char) (( ((unsigned int) (c1))- ((unsigned int) ('A')))+ ((unsigned int) ('a'))))))) || (((c2 >= 'A') && (c2 <= 'Z')) && (c1 == ((char) (( ((unsigned int) (c2))- ((unsigned int) ('A')))+ ((unsigned int) ('a')))))))
            {
              pi += 1;
              pj += 1;
              c1 = (*pi);
              c2 = (*pj);
            }
          else
            {
              /* difference found  */
              return false;
            }
        }
      return c1 == c2;
    }
  /* static analysis guarentees a RETURN statement will be used before here.  */
  __builtin_unreachable ();
}


/*
   keyToCharStar - returns the C char * string equivalent for, key.
*/

extern "C" void * nameKey_keyToCharStar (nameKey_Name key)
{
  if ((key == nameKey_NulName) || (! (Indexing_InBounds (keyIndex, key))))
    {
      return NULL;
    }
  else
    {
      return Indexing_GetIndice (keyIndex, key);
    }
  /* static analysis guarentees a RETURN statement will be used before here.  */
  __builtin_unreachable ();
}

extern "C" void _M2_nameKey_init (__attribute__((unused)) int argc,__attribute__((unused)) char *argv[],__attribute__((unused)) char *envp[])
{
  lastIndice = 0;
  keyIndex = Indexing_InitIndex (1);
  Storage_ALLOCATE ((void **) &binaryTree, sizeof (nameKey__T1));
  binaryTree->left = NULL;
}

extern "C" void _M2_nameKey_fini (__attribute__((unused)) int argc,__attribute__((unused)) char *argv[],__attribute__((unused)) char *envp[])
{
}
